﻿using IGGCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IGG.Data;

namespace IGG.Formatters
{
    public class StatManager
    {
        private class Randomizer
        {
            static Random rnd = new Random();
            static List<string> picked = new List<string>();

            public static InternalStat Pick(List<InternalStat> list)
            {
                InternalStat pickedStat = null;
                if (picked.Count == list.Count) // PICKED LIST IS FULL
                {
                    picked.Clear();
                }
                pickedStat = list[rnd.Next(list.Count)];
                while (picked.Contains(pickedStat.Key) == true)
                {
                    pickedStat = list[rnd.Next(list.Count)];
                }
                picked.Add(pickedStat.Key);
                return pickedStat;
            }
        }

        private List<InternalStat> internalStats = new List<InternalStat>();
        private List<InternalStat> SequentialStats = new List<InternalStat>();
        private Random rnd = new Random();

        public StatManager()
        {
            internalStats.Add(new InternalStat("Games Played", "G", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Oldest Players", "Age", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Plate Appearances", "PA", "Batting", null, false, true));
            internalStats.Add(new InternalStat("At Bats", "AB", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Runs", "R", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Hits", "H", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Doubles", "2B", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Triples", "3B", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Home Runs", "HR", "Batting", null, false, true));
            internalStats.Add(new InternalStat("RBI", "RBI", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Stolen Bases", "SB", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Caught Stealing", "CS", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Walks", "BB", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Strikeouts", "SO", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Batting Average", "BA", "Batting", null, true, true));
            internalStats.Add(new InternalStat("On Base %", "OBP", "Batting", null, true, true));
            internalStats.Add(new InternalStat("Slugging %", "SLG", "Batting", null, true, true));
            internalStats.Add(new InternalStat("OPS", "OPS", "Batting", null, true, true));
            internalStats.Add(new InternalStat("OPS+", "OPS+", "Batting", null, true, true));
            internalStats.Add(new InternalStat("Total Bases", "TB", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Grounded Double Plays", "GDP", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Hit By Pitches", "HBP", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Sacrifice Hits", "SH", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Sacrifice Flies", "SF", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Intentional Walks", "IBB", "Batting", null, false, true));
            internalStats.Add(new InternalStat("Wins", "W", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Losses", "L", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Games Pitched", "G", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Games Started", "GS", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("ERA", "ERA", "Pitching", null, true, false));
            internalStats.Add(new InternalStat("Win-Loss %", "W-L%", "Pitching", null, true, true));
            internalStats.Add(new InternalStat("Games Finished", "GF", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Completed Games", "CG", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Shutouts", "SHO", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Saves", "SV", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Innings Pitched", "IP", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Hits Allowed", "H", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Earned Runs Allowed", "ER", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Home Runs Allowed", "HR", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Walks", "BB", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Strikeouts", "SO", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Hit by a Pitch", "HBP", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Wild Pitches", "WP", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("WHIP", "WHIP", "Pitching", null, true, false));
            //internalStats.Add(new InternalStat("Strikeouts per 9IP", "SO/9", "Pitching", null, true, true));
            //internalStats.Add(new InternalStat("Hits per 9IP", "H/9", "Pitching", null, true, true));
            //internalStats.Add(new InternalStat("Strikeouts/Walks", "SO/BB", "Pitching", null, true, true));
            internalStats.Add(new InternalStat("ERA+", "ERA+", "Pitching", null, false, true));
            internalStats.Add(new InternalStat("Errors", "E", "Fielding", null, false, true));
            internalStats.Add(new InternalStat("Putouts", "PO", "Fielding", null, false, true));
            internalStats.Add(new InternalStat("Assists", "A", "Fielding", null, false, true));
            internalStats.Add(new InternalStat("Double Plays Turned", "DP", "Fielding", null, false, true));
            internalStats.Add(new InternalStat("Fielding %", "Fld%", "Fielding", null, false, true));
            //internalStats.Add(new InternalStat("Fielding %", "Fld%", "Fielding", null, false, true));

            // FAVORITE STATS 8 by League / 8 MLB
            SequentialStats.Add(new InternalStat("OPS+", "OPS+", "Batting", null, true, true));
            SequentialStats.Add(new InternalStat("ERA+", "ERA+", "Pitching", null, false, true));
            SequentialStats.Add(new InternalStat("Home Runs", "HR", "Batting", null, false, true));
            SequentialStats.Add(new InternalStat("WHIP", "WHIP", "Pitching", null, true, false));

            SequentialStats.Add(new InternalStat("Hits", "H", "Batting", null, false, true));
            SequentialStats.Add(new InternalStat("Strikeouts", "SO", "Pitching", null, false, true));
            SequentialStats.Add(new InternalStat("Stolen Bases", "SB", "Batting", null, false, true));
            SequentialStats.Add(new InternalStat("Wins", "W", "Pitching", null, false, true));


            SequentialStats.Add(new InternalStat("Walks", "BB", "Batting", null, false, true));
            SequentialStats.Add(new InternalStat("Errors", "E", "Fielding", null, false, true));
            SequentialStats.Add(new InternalStat("Innings Pitched", "IP", "Pitching", null, false, true));
            SequentialStats.Add(new InternalStat("Total Bases", "TB", "Batting", null, false, true));

            SequentialStats.Add(new InternalStat("Batting Average", "BA", "Batting", null, true, true));
            SequentialStats.Add(new InternalStat("Doubles", "2B", "Batting", null, false, true));
            SequentialStats.Add(new InternalStat("On Base %", "OBP", "Batting", null, true, true));
            SequentialStats.Add(new InternalStat("Walks", "BB", "Pitching", null, false, true));
        }

        public SortedStat[] GetRandomPairStats(Scoreboard board)
        {
            SortedStat[] stats = new SortedStat[2];
            InternalStat stat = Randomizer.Pick(internalStats);
            stats[0] = FormatStat(board, stat, "NL");
            stats[1] = FormatStat(board, stat, "AL");
            return stats;
        }

        public SortedStat GetRandomMLBStat(Scoreboard board)
        {
            InternalStat stat = Randomizer.Pick(internalStats);
            SortedStat sortedStat = FormatStat(board, stat, "MLB");
            return sortedStat;
        }

        private string GetSuffix(int id)
        {
            if (id == 1)
            {
                return "st";
            }
            else if (id == 3)
            {
                return "rd";
            }
            else if (id == 2)
            {
                return "nd";
            }
            else
            {
                return "th";
            }
        }

        public void TeamCrunchStats(Scoreboard board)
        {
            SortedStat stats = FormatStat(board, new InternalStat("Home Runs", "HR", "Batting", null, false, true), "MLB");
            IEnumerator<Dictionary<string, object>> enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i+1) + " in the league in homeruns with " + enumSorted.Current["HR"]);
            }

            stats = FormatStat(board, new InternalStat("RBI", "RBI", "Batting", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in RBI with " + enumSorted.Current["RBI"]);
            }

            stats = FormatStat(board, new InternalStat("Hits", "H", "Batting", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in hits with " + enumSorted.Current["H"]);
            }

            stats = FormatStat(board, new InternalStat("Stolen Bases", "SB", "Batting", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in stolen bases with " + enumSorted.Current["SB"]);
            }

            stats = FormatStat(board, new InternalStat("Strikeouts", "SO", "Pitching", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in strikeouts with " + enumSorted.Current["SO"]);
            }

            stats = FormatStat(board, new InternalStat("Saves", "SV", "Pitching", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in saves with " + enumSorted.Current["SV"]);
            }

            stats = FormatStat(board, new InternalStat("ERA+", "ERA+", "Pitching", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in ERA+ with " + enumSorted.Current["ERA+"]);
            }

            stats = FormatStat(board, new InternalStat("Innings Pitched", "IP", "Pitching", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in innings pitched with " + enumSorted.Current["IP"]);
            }

            stats = FormatStat(board, new InternalStat("WHIP", "WHIP", "Pitching", null, true, false), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in WHIP with " + enumSorted.Current["WHIP"]);
            }

            stats = FormatStat(board, new InternalStat("OPS+", "OPS+", "Batting", null, true, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in OPS+ with " + enumSorted.Current["OPS+"]);
            }

            stats = FormatStat(board, new InternalStat("On Base %", "OBP", "Batting", null, true, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in On Base % with " + enumSorted.Current["OBP"]);
            }

            stats = FormatStat(board, new InternalStat("Doubles", "2B", "Batting", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in doubles with " + enumSorted.Current["2B"]);
            }

            stats = FormatStat(board, new InternalStat("Triples", "3B", "Batting", null, false, true), "MLB");
            enumSorted = stats.SortedList.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumSorted.MoveNext();
                TeamCruncher.AddTeamLeaderStats(enumSorted.Current["Tm"].ToString(), enumSorted.Current["Player"] + " is " + (i + 1) + GetSuffix(i + 1) + " in the league in triples with " + enumSorted.Current["3B"]);
            }
        }

        private SortedStat FormatStat(Scoreboard board, InternalStat internalStat, string league)
        {
            SortedStat stat = new SortedStat();
            if (league == null)
            {
                int num = rnd.Next(100);
                if (num < 20)
                {
                    league = "NL";
                }
                else if (num < 40)
                {
                    league = "AL";
                }
                else
                {
                    league = "MLB";
                }
            }
            stat.Key = internalStat.Key;
            stat.Label = league + " " + internalStat.Label;
            List<Dictionary<string, object>> statList = board.StatsBatting;

            if (internalStat.Type == "Pitching")
            {
                statList = board.StatsPitching;
            }
            else if (internalStat.Type == "Fielding")
            {
                statList = board.StatsFielding;
            }

            if (internalStat.Desc == true)
            {
                if (league != "MLB")
                {
                    var sorted3 = statList.Where(dict => dict["Lg"].ToString() == league);
                    if (internalStat.MinimumPA)
                    {
                        if (internalStat.Type == "Batting")
                        {
                            sorted3 = sorted3.Where(dict => (float)dict["PA"] > StatisticsParser.AVG_PA);
                        } 
                        else if (internalStat.Type == "Pitching")
                        {
                            sorted3 = sorted3.Where(dict => (float)dict["IP"] > StatisticsParser.AVG_IP);
                        }
                        
                    }
                    stat.SortedList = sorted3.OrderByDescending(dict => dict[stat.Key]);
                }
                else
                {
                    if (internalStat.MinimumPA)
                    {
                        if (internalStat.Type == "Batting")
                        {
                            var sorted3 = statList.Where(dict => (float)dict["PA"] > StatisticsParser.AVG_PA);
                            stat.SortedList = sorted3.OrderByDescending(dict => dict[stat.Key]);
                        }
                        else if (internalStat.Type == "Pitching")
                        {
                            var sorted3 = statList.Where(dict => (float)dict["IP"] > StatisticsParser.AVG_IP);
                            stat.SortedList = sorted3.OrderByDescending(dict => dict[stat.Key]);
                        }

                    }
                    else
                    {
                        stat.SortedList = statList.OrderByDescending(dict => dict[stat.Key]);
                    }
                }
            }
            else
            {
                if (league != "MLB")
                {
                    var sorted3 = statList.Where(dict => dict["Lg"].ToString() == league);
                    if (internalStat.MinimumPA)
                    {
                        if (internalStat.Type == "Batting")
                        {
                            sorted3 = sorted3.Where(dict => (float)dict["PA"] > StatisticsParser.AVG_PA);
                        }
                        else if (internalStat.Type == "Pitching")
                        {
                            sorted3 = sorted3.Where(dict => (float)dict["IP"] > StatisticsParser.AVG_IP);
                        }
                    }
                    stat.SortedList = sorted3.OrderBy(dict => dict[stat.Key]);
                }
                else
                {
                    if (internalStat.MinimumPA)
                    {
                        if (internalStat.Type == "Batting")
                        {
                            var sorted3 = statList.Where(dict => (float)dict["PA"] > StatisticsParser.AVG_PA);
                            stat.SortedList = sorted3.OrderBy(dict => dict[stat.Key]);
                        }
                        else if (internalStat.Type == "Pitching")
                        {
                            var sorted3 = statList.Where(dict => (float)dict["IP"] > StatisticsParser.AVG_IP);
                            stat.SortedList = sorted3.OrderBy(dict => dict[stat.Key]);
                        }
                    }
                    else
                    {
                        stat.SortedList = statList.OrderBy(dict => dict[stat.Key]);
                    }
                }
            }

            return stat;
        }

        public SortedStat GetRandomStat(Scoreboard board)
        {
            int index = rnd.Next(internalStats.Count);
            return FormatStat(board, internalStats[index], null);
        }

        public class InternalStat
        {
            public string Label { get; set; }
            public string Key { get; set; }
            public string Type { get; set; }
            public bool Desc { get; set; }

            public bool MinimumPA { get; set; }
            public string Filter { get; set; }

            public InternalStat(string label, string key, string type, string filter, bool minPA, bool desc)
            {
                Label = label;
                Key = key;
                Filter = filter;
                Type = type;
                MinimumPA = minPA;
                Desc = desc;
            }
        }
    }
}
